package com.gitee.huanminabc.utils_tools.redis.redisTemplate.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Redis布隆过滤器工具类
 *
 * <p>使用 Redis BitSet 实现的布隆过滤器，内存占用小，适合大数据量场景。</p>
 *
 * <p><b>重要特性：</b></p>
 * <ul>
 *   <li><b>返回 false</b>：元素<b>肯定不存在</b>（100%准确，不会有误判）</li>
 *   <li><b>返回 true</b>：元素<b>可能存在</b>（可能有假阳性误判，需要用数据库等可靠数据源二次确认）</li>
 * </ul>
 *
 * <p><b>误判方向说明：</b></p>
 * <ul>
 *   <li>✅ <b>不会有假阴性</b>：不会把"存在的"误判为"不存在"（这是致命问题，但布隆过滤器保证不会发生）</li>
 *   <li>⚠️ <b>可能有假阳性</b>：可能把"不存在的"误判为"存在"（这个可以兜底，比如查询数据库确认）</li>
 * </ul>
 *
 * <p><b>使用方式：</b></p>
 * <pre>{@code
 * // 方式1：使用默认配置
 * BloomFilterRedisUtil.BloomFilter bloomFilter = new BloomFilterRedisUtil().builder()
 *           .keyPrefix("bf:TradeFlowDSStrategy:").build();
 *
 * // 方式2：自定义配置
 * BloomFilter filter =  new BloomFilterRedisUtil().builder()
 *     .keyPrefix("bf:account:")
 *     .bitSetSize(100_000_000L)
 *     .hashCount(3)
 *     .hashSeeds(new int[]{31, 131, 1313})
 *     .build();
 *
 * // 使用
 * filter.add("element123");
 * boolean exists = filter.mightContain("element123");
 *
 * // 清空布隆过滤器（删除整个布隆过滤器）
 * filter.clear();
 * }</pre>
 *
 * @author sby-plugin-common
 * @since 1.0.0
 */
@Slf4j
public class BloomFilterRedisUtil {

    /**
     * 默认BitSet大小：1亿位
     */
    private static final long DEFAULT_BIT_SET_SIZE = 100_000_000L;

    /**
     * 默认Hash函数数量（使用多个hash函数降低误判率）
     */
    private static final int DEFAULT_HASH_COUNT = 3;

    /**
     * 默认Hash函数种子（不同的种子生成不同的hash值）
     */
    private static final int[] DEFAULT_HASH_SEEDS = {31, 131, 1313};


    /**
     * 创建布隆过滤器构建器
     *
     * @return 构建器实例
     */
    public static BloomFilterBuilder builder() {
        return new BloomFilterBuilder();
    }

    /**
     * 布隆过滤器构建器
     */
    public static class BloomFilterBuilder {
        private String keyPrefix;
        private long bitSetSize = DEFAULT_BIT_SET_SIZE;
        private int hashCount = DEFAULT_HASH_COUNT;
        private int[] hashSeeds = DEFAULT_HASH_SEEDS;

        /**
         * 设置Redis Key前缀（必需）
         *
         * @param keyPrefix Key前缀
         * @return 构建器自身
         */
        public BloomFilterBuilder keyPrefix(String keyPrefix) {
            this.keyPrefix = keyPrefix;
            return this;
        }

        /**
         * 设置BitSet大小（位数）
         *
         * @param bitSetSize BitSet大小
         * @return 构建器自身
         */
        public BloomFilterBuilder bitSetSize(long bitSetSize) {
            this.bitSetSize = bitSetSize;
            return this;
        }

        /**
         * 设置Hash函数数量
         *
         * @param hashCount Hash函数数量
         * @return 构建器自身
         */
        public BloomFilterBuilder hashCount(int hashCount) {
            this.hashCount = hashCount;
            return this;
        }

        /**
         * 设置Hash函数种子数组
         *
         * @param hashSeeds Hash函数种子数组
         * @return 构建器自身
         */
        public BloomFilterBuilder hashSeeds(int[] hashSeeds) {
            this.hashSeeds = hashSeeds;
            return this;
        }

        /**
         * 构建布隆过滤器实例
         *
         * @return 布隆过滤器实例
         * @throws IllegalArgumentException 如果keyPrefix为空
         */
        public BloomFilter build(RedisTemplateUtil redisUtil) {
            if (keyPrefix == null || keyPrefix.isEmpty() || redisUtil == null) {
                throw new IllegalArgumentException("参数不能为空");
            }
            return new BloomFilter(keyPrefix, bitSetSize, hashCount, hashSeeds, redisUtil);
        }

        public BloomFilter buildAndClear(RedisTemplateUtil redisUtil) {
            if (keyPrefix == null || keyPrefix.isEmpty() || redisUtil == null) {
                throw new IllegalArgumentException("参数不能为空");
            }
            BloomFilter bloomFilter = new BloomFilter(keyPrefix, bitSetSize, hashCount, hashSeeds, redisUtil);
            bloomFilter.clear();
            return bloomFilter;
        }
    }

    /**
     * 布隆过滤器实例
     */
    public static class BloomFilter {
        private final String keyPrefix;
        private final long bitSetSize;
        private final int hashCount;
        private final int[] hashSeeds;
        private final RedisTemplateUtil redisUtil;

        private BloomFilter(String keyPrefix, long bitSetSize, int hashCount, int[] hashSeeds, RedisTemplateUtil redisUtil) {
            this.keyPrefix = keyPrefix;
            this.bitSetSize = bitSetSize;
            this.hashCount = hashCount;
            this.hashSeeds = hashSeeds;
            this.redisUtil = redisUtil;
        }

        /**
         * 判断元素是否可能存在
         *
         * <p><b>重要特性：</b></p>
         * <ul>
         *   <li><b>返回 false</b>：元素<b>肯定不存在</b>（100%准确，不会有误判）</li>
         *   <li><b>返回 true</b>：元素<b>可能存在</b>（可能有假阳性误判，需要用数据库等可靠数据源二次确认）</li>
         * </ul>
         *
         * @param element 要判断的元素
         * @return true表示可能存在（需要二次确认），false表示肯定不存在（100%准确）
         */
        public boolean mightContain(String element) {
            if (element == null || element.isEmpty()) {
                return false;
            }

            try {
                // 检查所有hash位置的位是否都为1
                for (int i = 0; i < hashCount; i++) {
                    long bitIndex = getBitIndex(element, hashSeeds[i]);
                    Boolean bitValue = redisUtil.getBit(keyPrefix, bitIndex);
                    if (bitValue == null || !bitValue) {
                        // 如果任何一个位为0，则肯定不存在（100%准确，不会有假阴性）
                        return false;
                    }
                }
                // 所有位都为1，可能存在（但可能有假阳性，需要二次确认）
                return true;
            } catch (Exception e) {
                log.error("BloomFilter::mightContain 判断元素是否已存在失败，redisKey: {}, element: {}", keyPrefix, element, e);
                // 异常时返回false，走数据库查询兜底
                return false;
            }
        }

        /**
         * 利用函数如果存在就执行函数不存在就添加数据
         *
         * @param element
         * @param addThrowable 添加异常比如数据库的唯一索引触发后需要将数据添加到过滤器中
         * @param consumer     因为有可能出现误判所以需要二次确认这个需要自己查询数据库, 如果存在就返回true 不存在就返回false
         * @return 返回true 表示可以添加
         */
        public void mightContainAccurate(String element, Class<? extends RuntimeException> addThrowable, Supplier<Boolean> get, Runnable consumer) {
            Objects.requireNonNull(element);
            try {
                if (mightContain(element)) {
                    Boolean apply = get.get();
                    //数据源内一定存在
                    if (apply) {
                        return;
                    }
                    //二次确认后不存在那么就执行添加
                    consumer.run();
                } else {
                    consumer.run();
                }
                add(element);
            } catch (Throwable e) {
                //判断异常是否符合添加异常
                if (addThrowable.isAssignableFrom(e.getClass())) {
                    add(element);
                }else{
                    throw e;
                }
            }
        }

        /**
         * 将元素添加到布隆过滤器
         *
         * @param element 要添加的元素
         */
        public void add(String element) {
            if (element == null || element.isEmpty()) {
                return;
            }

            try {
                // 将所有hash位置的位设置为1
                for (int i = 0; i < hashCount; i++) {
                    long bitIndex = getBitIndex(element, hashSeeds[i]);
                    redisUtil.setBit(keyPrefix, bitIndex, true);
                }
                log.debug("BloomFilter::add 元素已加入布隆过滤器，redisKey: {}, element: {}", keyPrefix, element);
            } catch (Exception e) {
                log.error("BloomFilter::add 添加元素到布隆过滤器失败，redisKey: {}, element: {}", keyPrefix, element, e);
            }
        }

        /**
         * 批量添加元素到布隆过滤器
         *
         * @param elements 要添加的元素列表
         */
        public void addBatch(List<String> elements) {
            if (elements == null || elements.isEmpty()) {
                return;
            }
            for (String element : elements) {
                add(element);
            }
        }

        /**
         * 计算元素在BitSet中的位置
         * 使用MD5 + hash种子计算，确保一致性
         *
         * @param element 元素
         * @param seed    hash种子
         * @return BitSet中的位置索引（0到bitSetSize-1）
         */
        private long getBitIndex(String element, int seed) {
            try {
                // 使用MD5生成hash值
                MessageDigest md5 = MessageDigest.getInstance("MD5");
                byte[] hashBytes = md5.digest((element + seed).getBytes(StandardCharsets.UTF_8));

                // 将hash值转换为long，并取模
                long hashValue = 0;
                for (int i = 0; i < Math.min(8, hashBytes.length); i++) {
                    hashValue = (hashValue << 8) | (hashBytes[i] & 0xFF);
                }

                // 确保结果为正数并取模
                long index = Math.abs(hashValue) % bitSetSize;
                return index;
            } catch (NoSuchAlgorithmException e) {
                // MD5算法不存在（理论上不会发生），使用简单hash
                long hashValue = (element + seed).hashCode();
                return Math.abs(hashValue) % bitSetSize;
            }
        }

        /**
         * 计算布隆过滤器的误判率（假阳性率）
         * 公式：p ≈ (1 - e^(-kn/m))^k
         *
         * @param expectedElements 预期插入的元素数量
         * @return 误判率（0.0到1.0之间，例如0.01表示1%的误判率）
         */
        public double calculateFalsePositiveRate(long expectedElements) {
            if (bitSetSize <= 0 || hashCount <= 0 || expectedElements <= 0) {
                return 1.0;
            }
            // p ≈ (1 - e^(-kn/m))^k
            double exponent = -(hashCount * expectedElements) / (double) bitSetSize;
            double base = 1 - Math.exp(exponent);
            return Math.pow(base, hashCount);
        }

        /**
         * 获取当前配置的BitSet大小
         *
         * @return BitSet大小（位数）
         */
        public long getBitSetSize() {
            return bitSetSize;
        }

        /**
         * 获取当前配置的Hash函数数量
         *
         * @return Hash函数数量
         */
        public int getHashCount() {
            return hashCount;
        }

        /**
         * 获取当前配置的Key前缀
         *
         * @return Key前缀
         */
        public String getKeyPrefix() {
            return keyPrefix;
        }

        /**
         * 清空布隆过滤器（删除整个布隆过滤器）
         *
         * <p>注意：此操作会删除Redis中的整个布隆过滤器，删除后需要重新添加元素。</p>
         *
         * @return true表示删除成功，false表示删除失败或key不存在
         */
        public boolean clear() {
            try {
                boolean deleted = redisUtil.del(keyPrefix);
                if (deleted) {
                    log.debug("BloomFilter::clear 布隆过滤器已清空，redisKey: {}", keyPrefix);
                } else {
                    log.debug("BloomFilter::clear 布隆过滤器不存在或删除失败，redisKey: {}", keyPrefix);
                }
                return deleted;
            } catch (Exception e) {
                log.error("BloomFilter::clear 清空布隆过滤器失败，redisKey: {}", keyPrefix, e);
                return false;
            }
        }
    }

    /**
     * 计算布隆过滤器的误判率（假阳性率）
     * 公式：p ≈ (1 - e^(-kn/m))^k
     *
     * @param bitSetSize       BitSet大小（位数）
     * @param hashCount        Hash函数数量
     * @param expectedElements 预期插入的元素数量
     * @return 误判率（0.0到1.0之间，例如0.01表示1%的误判率）
     */
    public static double calculateFalsePositiveRate(long bitSetSize, int hashCount, long expectedElements) {
        if (bitSetSize <= 0 || hashCount <= 0 || expectedElements <= 0) {
            return 1.0;
        }
        // p ≈ (1 - e^(-kn/m))^k
        double exponent = -(hashCount * expectedElements) / (double) bitSetSize;
        double base = 1 - Math.exp(exponent);
        return Math.pow(base, hashCount);
    }

    /**
     * 根据预期的元素数量和目标误判率，计算最优的BitSet大小
     * 公式：m = -n * ln(p) / (ln(2)^2)
     *
     * @param expectedElements        预期插入的元素数量
     * @param targetFalsePositiveRate 目标误判率（例如0.01表示1%）
     * @return 推荐的BitSet大小（位数）
     */
    public static long calculateOptimalBitSetSize(long expectedElements, double targetFalsePositiveRate) {
        if (expectedElements <= 0 || targetFalsePositiveRate <= 0 || targetFalsePositiveRate >= 1) {
            return DEFAULT_BIT_SET_SIZE;
        }
        // m = -n * ln(p) / (ln(2)^2)
        double ln2 = Math.log(2);
        double lnp = Math.log(targetFalsePositiveRate);
        return (long) Math.ceil(-expectedElements * lnp / (ln2 * ln2));
    }

    /**
     * 根据预期的元素数量和BitSet大小，计算最优的Hash函数数量
     * 公式：k = (m/n) * ln(2)
     *
     * @param expectedElements 预期插入的元素数量
     * @param bitSetSize       BitSet大小（位数）
     * @return 推荐的Hash函数数量
     */
    public static int calculateOptimalHashCount(long expectedElements, long bitSetSize) {
        if (expectedElements <= 0 || bitSetSize <= 0) {
            return DEFAULT_HASH_COUNT;
        }
        // k = (m/n) * ln(2)
        double optimalK = (bitSetSize / (double) expectedElements) * Math.log(2);
        return Math.max(1, (int) Math.round(optimalK));
    }
}
